from mcp.server import FastMCP
from pan123 import Pan123

from dotenv import load_dotenv
import os
import platform
from typing import Union

import http.client
import json
import os
from datetime import datetime, timezone
from dotenv import load_dotenv, set_key
load_dotenv(dotenv_path="./.env")


def get_123_token(client_id="123456789", client_secret="123456789"):
    """
    获取123Pan的access token
    如果.env中存在未过期的token则直接返回，否则获取新token并更新.env

    参数:
        client_id (str): 123Pan开放平台的clientID
        client_secret (str): 123Pan开放平台的clientSecret

    返回:
        tuple: (access_token, expired_at) 或 (None, None) 如果获取失败
    """

    # 检查现有token是否有效
    current_token = os.getenv("ACCESS_TOKEN")
    expired_at = os.getenv("EXPIRED_AT")

    if current_token and expired_at and _is_token_valid(expired_at):
        # print("使用现有的有效token")
        return current_token, expired_at

    # print("获取新的access token...")
    new_token, new_expired_at = _fetch_new_token(client_id, client_secret)

    if new_token and new_expired_at:
        # 保存到.env文件
        set_key(".env", "ACCESS_TOKEN", new_token)
        set_key(".env", "EXPIRED_AT", new_expired_at)
        # print("成功获取并保存新的access token")
        return new_token, new_expired_at

    # print("未能获取有效的access token")
    return None, None


def _fetch_new_token(client_id, client_secret):
    """从123Pan API获取新的access token"""
    conn = http.client.HTTPSConnection("open-api.123pan.com")
    payload = json.dumps({
        "clientID": client_id,
        "clientSecret": client_secret
    })
    headers = {
        'Platform': 'open_platform',
        'Content-Type': 'application/json'
    }

    try:
        conn.request("POST", "/api/v1/access_token", payload, headers)
        res = conn.getresponse()
        data = res.read()
        response = json.loads(data.decode("utf-8"))

        if response.get("code") == 0:
            return response["data"]["accessToken"], response["data"]["expiredAt"]
        else:
            print(f"获取access token失败: {response.get('message')}")
            return None, None
    except Exception as e:
        print(f"请求发生错误: {str(e)}")
        return None, None
    finally:
        conn.close()


def _is_token_valid(expired_at):
    """检查token是否过期"""
    if not expired_at:
        return False

    try:
        expired_time = datetime.fromisoformat(expired_at)
        return datetime.now(timezone.utc) < expired_time
    except ValueError:
        return False


token, expired_at = get_123_token()

pan123 = Pan123(token)

# 初始化MCP服务器
mcp = FastMCP("Hello-mcp-server")
mcp.version = "1.2.0"
mcp.description = "123云盘MCP服务器"

@mcp.tool(name="user info",  description="获取用户信息")
def get_user_info() -> dict:
    user_info = pan123.user.info()
    return {"data": user_info}

# 创建目录
@mcp.tool(name="mkdir", description="创建目录")
def mkdir(name: str, parent_id: int = 0) -> dict:
    """
    创建目录。

    :param name: 目录名称
    :param parent_id: 父目录ID
    :return: API响应
    """
    return pan123.file.mkdir(name, parent_id)

@mcp.tool(name="list files", description="列出文件")
def list_files(parent_id: int = 0, limit: int = 100, search_data=None, search_mode=None, last_file_id=None) -> dict:
    """
    列出文件。

    :param parent_id: 父目录ID
    :param limit: 每页数量
    :param search_data: 搜索数据
    :param search_mode: 搜索模式
    :param last_file_id: 最后一个文件ID
    :return: API响应
    """
    return pan123.file.list(parent_id, limit, search_data, search_mode, last_file_id)


# 上传单文件
@mcp.tool(name="upload file", description="上传文件")
def upload_file(file_path: str, parent_id: int = 0) -> dict:
    """
    上传文件。

    :param file_path: 文件路径,绝对路径
    :param parent_id: 父目录ID
    :return: API响应
    """
    return pan123.file.upload(parent_id, file_path)

# 上传目录
@mcp.tool(name="upload directory", description="上传目录")
def upload_directory(directory_path: str, parent_id: int = 0) -> dict:
    """
    上传目录。

    :param directory_path: 目录路径，绝对路径
    :param parent_id: 父目录ID
    :return: API响应
    """
    return pan123.file.upload_directory(directory_path, parent_id)

# 重命名文件/目录
@mcp.tool(name="rename file", description="重命名文件/目录")
def rename_file(rename_dict: dict) -> dict:
    """
    重命名文件/目录。
    :param rename_dict: 文件/目录ID和名称的字典, 比如{"file_id": "new_name"}
    :return: API响应
    """
    return pan123.file.rename(rename_dict)

# 移动文件/目录
@mcp.tool(name="move files", description="移动文件/目录")
def move_files(file_ids: list, to_parent_file_id: int) -> dict:
    """
    移动文件/目录。

    :param file_ids: 文件/目录ID列表
    :param to_parent_file_id: 目标父目录ID
    :return: API响应
    """
    return pan123.file.move(file_ids, to_parent_file_id)


# 移动垃圾桶
@mcp.tool(name="trash files", description="删除文件至回收站")
def trash_files(file_ids: list) -> dict:
    """
    移动垃圾桶。

    :param file_ids: 文件/目录ID列表
    :return: API响应
    """
    return pan123.file.trash(file_ids)

# 从回收站恢复文件
@mcp.tool(name="recover files", description="从回收站恢复文件")
def recover_files(file_ids: list) -> dict:
    """
    从回收站恢复文件。

    :param file_ids: 文件/目录ID列表
    :return: API响应
    """
    return pan123.file.recover(file_ids)

# 彻底删除文件
@mcp.tool(name="delete files", description="彻底删除文件，注意：彻底删除文件前，文件必须要在回收站中，否则无法删除")
def delete_files(file_ids: list) -> dict:
    """
    彻底删除文件。

    :param file_ids: 文件/目录ID列表
    :return: API响应
    """
    return pan123.file.delete(file_ids)

# 创建分享链接
# create(self, share_name: str, share_expire: int, file_id_list: list, share_pwd=None, traffic_switch=None,
#                traffic_limit_switch=None, traffic_limit=None):
@mcp.tool(name="create share", description="创建分享链接")
def create_share(share_name: str, share_expire: int, file_id_list: list, share_pwd: str = None, traffic_switch: int = 1,
                 traffic_limit_switch: int = 1, traffic_limit: int = 0) -> dict:
    """
    创建分享链接。

    :param share_name: 分享链接名称
    :param share_expire: 分享链接有效期
    :param file_id_list: 文件ID列表
    :param share_pwd: 分享链接提取码
    :param traffic_switch: 流量开关
    :param traffic_limit_switch: 流量限制开关
    :param traffic_limit: 流量限制
    :return: API响应
    """
    return pan123.share.create(share_name, share_expire, file_id_list, share_pwd, traffic_switch, traffic_limit_switch,
                               traffic_limit)

# 修改分析连接
# def list_info(self, share_id_list: list, traffic_switch: bool = None, traffic_limit_switch: bool = None,
#                   traffic_limit: int = None):
@mcp.tool(name="modify share", description="修改分享链接")
def modify_share(share_id_list: list, traffic_switch: int = 1, traffic_limit_switch: int = 1, traffic_limit: int = 0) -> dict:
    """
    修改分享链接。
    :param share_id_list: 分享链接ID列表
    :param traffic_switch: 流量开关
    :param traffic_limit_switch: 流量限制开关
    :param traffic_limit: 流量限制
    :return: API响应
    """
    return pan123.share.list_info(share_id_list, traffic_switch, traffic_limit_switch, traffic_limit)

# 获取分享链接列表
# def list(self, limit: int, last_share_id: int = None):
@mcp.tool(name="list shares", description="获取分享链接 ")
def list_shares(limit: int = 100,  last_share_id: int = None) -> dict:
    """
    获取分享链接列表。

    :param limit: 每页数量
    :param last_share_id: 最后一个分享链接ID
    :return: API响应
    """
    return pan123.share.list(limit, last_share_id)

# 创建付费链接
# def create_paid_share(self, share_name: str, file_id_list: list, pay_amount: int, is_reward: int = 0,
#                          resource_desc: str = ""):
@mcp.tool(name="create paid share", description="创建付费分享链接")
def create_paid_share(share_name: str, file_id_list: list, pay_amount: int, is_reward: int = 0,
                      resource_desc: str = "") -> dict:
    """
    创建付费链接。

    :param share_name: 分享链接名称
    :param file_id_list: 文件ID列表
    :param pay_amount: 支付金额
    :param is_reward: 是否打赏
    :param resource_desc: 资源描述，必填
    :return: API响应
    """
    return pan123.share.create_paid_share(share_name, file_id_list, pay_amount, is_reward, resource_desc)


# 启用直链功能
@mcp.tool(name="enable direct link", description="启用直链功能")
def enable_direct_link(file_id) -> dict:
    """
    启用直链功能。

    :param file_id: 文件ID
    :return: API响应
    """
    return pan123.direct_link.enable(file_id)


# 关闭直链功能
@mcp.tool(name="disable direct link", description="关闭直链功能")
def disable_direct_link(file_id) -> dict:
    """
    关闭直链功能。
    :param file_id: 文件ID
    :return: API响应
    """
    return pan123.direct_link.disable(file_id)


# 获取直链
@mcp.tool(name="get direct link", description="获取直链")
def get_direct_link(file_id) -> dict:
    """
    获取直链。
    :param file_id: 文件ID
    :return: API响应
    """
    return pan123.direct_link.list_url(file_id)

# 下载文件--单个文件
# def download(self, file_id, save_dir=None):



@mcp.tool(name="download file", description="下载文件")
def download_file(file_id: int, save_dir: Union[str, None] = None) -> dict:
    """
    下载文件到指定目录，如果目录为None则根据操作系统自动选择默认下载目录

    :param file_id: 要下载的文件ID
    :param save_dir: 保存目录路径，如果为None则自动选择系统下载目录
    :return: 包含下载结果的字典，格式为:
        {
            "success": bool,  # 是否下载成功
            "message": str,    # 结果消息
            "file_path": str,  # 文件保存路径
            "file_id": int     # 文件ID
        }
    """
    try:
        # 如果未指定保存目录，根据操作系统选择默认下载目录
        if save_dir is None:
            system = platform.system()

            if system == "Windows":
                # Windows系统通常将下载目录放在用户目录下的Downloads文件夹
                save_dir = os.path.join(os.path.expanduser("~"), "Downloads")
            elif system == "Linux":
                # Linux系统通常将下载目录放在用户目录下的Downloads文件夹
                save_dir = os.path.join(os.path.expanduser("~"), "Downloads")
            elif system == "Darwin":  # macOS
                # macOS系统通常将下载目录放在用户目录下的Downloads文件夹
                save_dir = os.path.join(os.path.expanduser("~"), "Downloads")
            else:
                # 其他系统使用当前目录
                save_dir = os.getcwd()

        # 确保目录存在
        os.makedirs(save_dir, exist_ok=True)

        # 调用原始下载方法
        file_path = pan123.file.download(file_id, save_dir)

        return {
            "success": True,
            "message": "文件下载成功",
            "file_path": file_path,
            "file_id": file_id
        }
    except Exception as e:
        return {
            "success": False,
            "message": f"文件下载失败: {str(e)}",
            "file_path": "",
            "file_id": file_id
        }




if __name__ == "__main__":
    mcp.run(transport='stdio')
